package com.yehebl.orm.manager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.yehebl.orm.anno.Column;
import com.yehebl.orm.anno.Dto;
import com.yehebl.orm.anno.Entity;
import com.yehebl.orm.anno.Id;
import com.yehebl.orm.config.IConfig;
import com.yehebl.orm.dto.ClassInfo;
import com.yehebl.orm.dto.FieldInfo;
import com.yehebl.orm.enums.IdType;
import com.yehebl.orm.util.LogUtil;

/**
 * 读取类信息
 *
 * @author tzd
 */
public class ReadClassManager {
    /**
     * key:包名加类名 value：类型详细信息
     */
    public static Map<String, ClassInfo> map = new HashMap<>();


    private static Column defaultColumn = new Column() {
        @Override
        public Class<? extends Annotation> annotationType() {
            return Column.class;
        }

        @Override
        public String name() {
            return "";
        }
    };


    public static void readEntityClass(Class<?> clazz, IConfig config) {
        if (map.containsKey(clazz.getName())) {
            return;
        }
        Dto dto = clazz.getAnnotation(Dto.class);
        Entity entity = clazz.getAnnotation(Entity.class);
        Table table = clazz.getAnnotation(Table.class);
        if (dto != null) {
            MethodAccessManager.set(clazz);
            ClassInfo ci = new ClassInfo();
            ci.setConfig(config);
            map.put(clazz.getName(), ci);
            ci.setClazz(clazz);
            Set<String> setFields = new HashSet<>();
            readDtoClass(ci, clazz, config, setFields);
            return;
        }
        if (entity != null) {
            MethodAccessManager.set(clazz);
            ClassInfo ci = new ClassInfo();
            ci.setConfig(config);
            map.put(clazz.getName(), ci);
            ci.setTableName(config.classToTableRule(clazz)); //赋值默
            ci.setTableNameRule(entity.tableNameRule());// 认表名
            ci.setClazz(clazz);
            Set<String> setFields = new HashSet<>();
            readEntityClass(ci, clazz, config, setFields);
            return;
        }

        if (table != null) {
            LogUtil.printInfo("读取数据库对应实体类[" + clazz.getName() + "]信息");
            MethodAccessManager.set(clazz);
            ClassInfo ci = new ClassInfo();
            ci.setConfig(config);
            map.put(clazz.getName(), ci);
            ci.setTableName(config.classToTableRule(clazz)); //赋值默认表名
            ci.setClazz(clazz);
            Set<String> setFields = new HashSet<>();
            readJpaEntityClass(ci, clazz, config, setFields);
        }

    }


    /**
     * jpa 注解支持
     */
    private static void readJpaEntityClass(ClassInfo ci, Class<?> clazz, IConfig config, Set<String> setFields) {
        Table table = clazz.getAnnotation(Table.class);
        if (ci.getClazz().equals(clazz)) {
            //读取本类，如果有指定的表名这覆盖默认表名
            if (!"".equals(table.name())) {
                ci.setTableName(table.name());
            }
        }
        if (ci.getTableName() == null) {
            //DTO对象的父类为实体类
            ci.setTableName(config.classToTableRule(clazz)); //赋值默认表名
            //读取实体类，如果有指定的表名这覆盖默认表名
            if (!"".equals(table.name())) {
                ci.setTableName(table.name());
            }
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (setFields.contains(declaredField.getName())) {
                continue;
            }
            if (Modifier.isStatic(declaredField.getModifiers())) {
                continue;
            }

            setFields.add(declaredField.getName());
            Id id = declaredField.getAnnotation(Id.class);
            javax.persistence.Id jpaId = declaredField.getAnnotation(javax.persistence.Id.class);
            javax.persistence.Column jpaColumn = declaredField.getAnnotation(javax.persistence.Column.class);
            Transient jpaTransient = declaredField.getAnnotation(Transient.class);
            com.yehebl.orm.anno.Transient ormTransient = declaredField.getAnnotation(com.yehebl.orm.anno.Transient.class);

            FieldInfo fi = new FieldInfo();
            fi.setFieldType(declaredField.getType());
            fi.setFieldName(declaredField.getName());
            fi.setTableFieldName(config.fieldToColumnRule(clazz, fi.getFieldName()));
            ci.getAllFieldMap().put(declaredField.getName(), fi);


            if (ormTransient != null || jpaTransient!=null) {
                //忽略的字段
                continue;
            }
            if (jpaColumn != null) {
                if (!"".equals(jpaColumn.name())) {
                    fi.setTableFieldName(jpaColumn.name());
                }
            }
            ci.getTableFieldMap().put(fi.getTableFieldName(), fi);
            ci.getFieldMap().put(fi.getFieldName(), fi);


            if (jpaId != null) {
                GeneratedValue jpaGeneratedValue = declaredField.getAnnotation(GeneratedValue.class);
                fi.setIdType(IdType.auto);
                if (jpaGeneratedValue != null) {
                    if (jpaGeneratedValue.strategy().equals(GenerationType.IDENTITY)) {
                        fi.setIdType(IdType.generated);
                    }
                }
                ci.getIdFieldInfo().add(fi);
            }
            if (id != null) {
                fi.setIdType(id.value());
                ci.getIdFieldInfo().add(fi);
            }

        }
        Class<?> parentClazz = clazz.getSuperclass();
        if (parentClazz != null) {
            Entity entity = parentClazz.getAnnotation(Entity.class);
            if (entity != null) {
                readEntityClass(ci, parentClazz, config, setFields);
                return;
            }
            readJpaEntityClass(ci, parentClazz, config, setFields);
        }
    }


    private static void readEntityClass(ClassInfo ci, Class<?> clazz, IConfig config, Set<String> setFields) {
        Entity entity = clazz.getAnnotation(Entity.class);
        if (ci.getClazz().equals(clazz)) {
            //读取本类，如果有指定的表名这覆盖默认表名
            if (!"".equals(entity.value())) {
                ci.setTableName(entity.value());
            }
        }
        if (ci.getTableName() == null) {
            //DTO对象的父类为实体类
            ci.setTableName(config.classToTableRule(clazz)); //赋值默认表名
            //读取实体类，如果有指定的表名这覆盖默认表名
            if (!"".equals(entity.value())) {
                ci.setTableName(entity.value());
            }
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (setFields.contains(declaredField.getName())) {
                continue;
            }
            if(Modifier.isStatic(declaredField.getModifiers())){
                continue;
            }
            setFields.add(declaredField.getName());
            Id id = declaredField.getAnnotation(Id.class);
            Column column = declaredField.getAnnotation(Column.class);
            com.yehebl.orm.anno.Transient ormTransient = declaredField.getAnnotation(com.yehebl.orm.anno.Transient.class);
            Transient jpaTransient = declaredField.getAnnotation(Transient.class);
            FieldInfo fi = new FieldInfo();
            fi.setFieldType(declaredField.getType());
            fi.setFieldName(declaredField.getName());
            fi.setTableFieldName(config.fieldToColumnRule(clazz, fi.getFieldName()));
            ci.getAllFieldMap().put(declaredField.getName(), fi);
            if (ormTransient != null || jpaTransient!=null) {
                //忽略的字段
                continue;
            }
            if (column == null) {
                column = defaultColumn;
            }

            if (!"".equals(column.name())) {
                fi.setTableFieldName(column.name());
            }
            ci.getTableFieldMap().put(fi.getTableFieldName(), fi);
            ci.getFieldMap().put(fi.getFieldName(), fi);

            if (id != null) {
                fi.setIdType(id.value());
                ci.getIdFieldInfo().add(fi);
            }

        }
        Class<?> parentClazz = clazz.getSuperclass();
        if (parentClazz != null) {
            Table table = parentClazz.getAnnotation(Table.class);
            if (table != null) {
                readJpaEntityClass(ci, parentClazz, config, setFields);
                return;
            }
            readEntityClass(ci, parentClazz, config, setFields);
        }
    }


    private static void readDtoClass(ClassInfo ci, Class<?> clazz, IConfig config, Set<String> setFields) {
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            FieldInfo fi = new FieldInfo();
            fi.setFieldType(declaredField.getType());
            fi.setFieldName(declaredField.getName());
            fi.setTableFieldName(config.fieldToColumnRule(clazz, fi.getFieldName()));
            ci.getAllFieldMap().put(declaredField.getName(), fi);
        }
        Class<?> parentClazz = clazz.getSuperclass();
        if (parentClazz != null) {
            if (parentClazz.getAnnotation(Entity.class) != null) {
                readEntityClass(ci, parentClazz, config, setFields);
            } else {
                readDtoClass(ci, parentClazz, config, setFields);
            }
        }
    }

}
